        # 
        # file: kernelvec.S
        # date: 23.04.30
        # author: xin
        # brief: 内核中断异常入口以及M模式计时器中断入口
        # version: 0.1
        # status: Stable

.globl kerneltrap
.globl kernelvec
.align 4
kernelvec:
        # 给保存寄存器腾地方
        addi sp, sp, -256

        # 保存寄存器（上下文）
        sd ra, 0(sp)
        sd sp, 8(sp)
        sd gp, 16(sp)
        sd tp, 24(sp)
        sd t0, 32(sp)
        sd t1, 40(sp)
        sd t2, 48(sp)
        sd s0, 56(sp)
        sd s1, 64(sp)
        sd a0, 72(sp)
        sd a1, 80(sp)
        sd a2, 88(sp)
        sd a3, 96(sp)
        sd a4, 104(sp)
        sd a5, 112(sp)
        sd a6, 120(sp)
        sd a7, 128(sp)
        sd s2, 136(sp)
        sd s3, 144(sp)
        sd s4, 152(sp)
        sd s5, 160(sp)
        sd s6, 168(sp)
        sd s7, 176(sp)
        sd s8, 184(sp)
        sd s9, 192(sp)
        sd s10, 200(sp)
        sd s11, 208(sp)
        sd t3, 216(sp)
        sd t4, 224(sp)
        sd t5, 232(sp)
        sd t6, 240(sp)

        # 调用trap.c中的trap处理C代码
        call kerneltrap

        # 从trap处理代码返回后取回寄存器
        ld ra, 0(sp)
        ld sp, 8(sp)
        ld gp, 16(sp)
        # 此处不取回tp，以防已经换成其他cpu了
        ld t0, 32(sp)
        ld t1, 40(sp)
        ld t2, 48(sp)
        ld s0, 56(sp)
        ld s1, 64(sp)
        ld a0, 72(sp)
        ld a1, 80(sp)
        ld a2, 88(sp)
        ld a3, 96(sp)
        ld a4, 104(sp)
        ld a5, 112(sp)
        ld a6, 120(sp)
        ld a7, 128(sp)
        ld s2, 136(sp)
        ld s3, 144(sp)
        ld s4, 152(sp)
        ld s5, 160(sp)
        ld s6, 168(sp)
        ld s7, 176(sp)
        ld s8, 184(sp)
        ld s9, 192(sp)
        ld s10, 200(sp)
        ld s11, 208(sp)
        ld t3, 216(sp)
        ld t4, 224(sp)
        ld t5, 232(sp)
        ld t6, 240(sp)

        addi sp, sp, 256

        # 从S模式返回
        sret

        # 
        # M模式计时器中断
        # 
.globl timervec
.align 4
timervec:
        # start.c将mscratch指向了scratch
        # scratch[0,8,16]：寄存器保存区域
        # scratch[24]：CLINT's MTIMECMP寄存器地址
        # scratch[32]：每两个计时器中断间的间隔

        csrrw a0, mscratch, a0
        sd a1, 0(a0)
        sd a2, 8(a0)
        sd a3, 16(a0)

        # 依靠向MTIMECMP增加间隔调度下一个计时器中断
        ld a1, 24(a0)   # CLINT_MTIMECMP(hart)
        ld a2, 32(a0)   # interval
        ld a3, 0(a1)
        add a3, a3, a2
        sd a3, 0(a1)

        # 在这个处理结束返回后安排一个S模式软件中断(bit2)
        li a1, 2
        csrw sip, a1

        ld a3, 16(a0)
        ld a2, 8(a0)
        ld a1, 0(a0)
        csrrw a0, mscratch, a0

        mret